/*
 * Tint Browser for Android
 *
 * Copyright (C) 2012 - to infinity and beyond J. Devauchelle and contributors.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * version 3 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

package com.seal.ui.managers;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;

import android.annotation.SuppressLint;
import android.app.ActionBar;
import android.app.DownloadManager;
import android.app.FragmentManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences.Editor;
import android.graphics.Bitmap;
import android.graphics.Picture;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
import android.os.Handler;
import android.os.Message;
import android.preference.PreferenceManager;
import android.text.TextUtils;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.GeolocationPermissions.Callback;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebChromeClient.CustomViewCallback;
import android.webkit.WebViewDatabase;
import android.widget.FrameLayout;
import android.widget.Toast;

import com.mogoweb.chrome.WebView;
import com.mogoweb.chrome.WebView.HitTestResult;
import com.seal.R;
import com.seal.controllers.Controller;
import com.seal.model.DownloadItem;
import com.seal.providers.BookmarksWrapper;
import com.seal.tasks.ThumbnailSaver;
import com.seal.ui.activities.BookmarksActivity;
import com.seal.ui.activities.EditBookmarkActivity;
import com.seal.ui.activities.SealBrowserActivity;
import com.seal.ui.components.CustomWebView;
import com.seal.ui.dialogs.GeolocationPermissionsDialog;
import com.seal.ui.fragments.BaseWebViewFragment;
import com.seal.ui.fragments.StartPageFragment;
import com.seal.utils.ApplicationUtils;
import com.seal.utils.Constants;

@SuppressLint("HandlerLeak")
public abstract class BaseUIManager implements UIManager {// , WebViewFragmentListener {

	protected static final FrameLayout.LayoutParams COVER_SCREEN_PARAMS = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
			ViewGroup.LayoutParams.MATCH_PARENT);

	private static final int FOCUS_NODE_HREF = 102;

	private FrameLayout mFullscreenContainer;
	private View mCustomView;
	private WebChromeClient.CustomViewCallback mCustomViewCallback;
	private int mOriginalOrientation;

	private GeolocationPermissionsDialog mGeolocationPermissionsDialog;

	protected SealBrowserActivity mActivity;
	protected ActionBar mActionBar;
	protected FragmentManager mFragmentManager;

	protected boolean mHomePageLoading = false;

	protected boolean mMenuVisible = false;

	private ValueCallback<Uri> mUploadMessage = null;

	protected StartPageFragment mStartPageFragment = null;

	private Handler mHandler;

	public BaseUIManager(SealBrowserActivity activity) {
		mActivity = activity;

		mActionBar = mActivity.getActionBar();
		mFragmentManager = mActivity.getFragmentManager();

		mGeolocationPermissionsDialog = null;

		setupUI();

		startHandler();
	}

	protected abstract String getCurrentUrl();

	protected abstract int getTabCount();

	protected abstract BaseWebViewFragment getWebViewFragmentByUUID(UUID fragmentId);

	protected abstract void showStartPage(BaseWebViewFragment webViewFragment);

	protected abstract void hideStartPage(BaseWebViewFragment webViewFragment);

	protected abstract void resetUI();

	protected void setApplicationButtonImage(Bitmap icon) {
		BitmapDrawable image = ApplicationUtils.getApplicationButtonImage(mActivity, icon);

		if (image != null) {
			mActionBar.setIcon(image);
		} else {
			mActionBar.setIcon(R.drawable.ic_launcher);
		}
	}

	protected void setupUI() {
		setFullScreenFromPreferences();
	}

	@Override
	public SealBrowserActivity getMainActivity() {
		return mActivity;
	}

	@Override
	public void addTab(boolean loadHomePage, boolean privateBrowsing, boolean openByParent) {
		if (loadHomePage) {
			addTab(PreferenceManager.getDefaultSharedPreferences(mActivity).getString(Constants.PREFERENCE_HOME_PAGE, Constants.URL_ABOUT_START), false,
					privateBrowsing, openByParent);
		} else {
			addTab(null, false, privateBrowsing, openByParent);
		}
	}

	@Override
	public void togglePrivateBrowsing() {
		BaseWebViewFragment fragment = getCurrentWebViewFragment();
		if (fragment != null) {
			CustomWebView webView = fragment.getWebView();
			String currentUrl = webView.getUrl();

			fragment.setPrivateBrowsing(!fragment.isPrivateBrowsingEnabled());
			fragment.resetWebView();

			resetUI();
			loadUrl(currentUrl);
		}
	}

	@Override
	public void loadUrl(String url) {
		loadUrl(getCurrentWebViewFragment(), url);
	}

	@Override
	public void loadUrl(UUID tabId, String url, boolean loadInCurrentTabIfNotFound) {
		BaseWebViewFragment fragment = getWebViewFragmentByUUID(tabId);
		if (fragment != null) {
			loadUrl(fragment, url);
		} else {
			if (loadInCurrentTabIfNotFound) {
				loadUrl(url);
			}
		}
	}

	@Override
	public void loadRawUrl(UUID tabId, String url, boolean loadInCurrentTabIfNotFound) {
		BaseWebViewFragment fragment = getWebViewFragmentByUUID(tabId);
		if (fragment != null) {
			fragment.getWebView().loadRawUrl(url);
		} else {
			if (loadInCurrentTabIfNotFound) {
				getCurrentWebView().loadRawUrl(url);
			}
		}
	}

	@Override
	public void loadHomePage() {
		mHomePageLoading = true;
		loadUrl(PreferenceManager.getDefaultSharedPreferences(mActivity).getString(Constants.PREFERENCE_HOME_PAGE, Constants.URL_ABOUT_START));
	}

	@Override
	public void loadHomePage(UUID tabId, boolean loadInCurrentTabIfNotFound) {
		mHomePageLoading = true;
		loadUrl(tabId, PreferenceManager.getDefaultSharedPreferences(mActivity).getString(Constants.PREFERENCE_HOME_PAGE, Constants.URL_ABOUT_START),
				loadInCurrentTabIfNotFound);
	}

	@Override
	public void loadCurrentUrl() {
		loadUrl(getCurrentUrl());
	}

	@Override
	public void openBookmarksActivityForResult() {
		Intent i = new Intent(mActivity, BookmarksActivity.class);
		mActivity.startActivityForResult(i, SealBrowserActivity.ACTIVITY_BOOKMARKS);
	}

	@Override
	public void addBookmarkFromCurrentPage() {
		Intent i = new Intent(mActivity, EditBookmarkActivity.class);

		i.putExtra(Constants.EXTRA_ID, (long) -1);
		i.putExtra(Constants.EXTRA_LABEL, getCurrentWebView().getTitle());
		i.putExtra(Constants.EXTRA_URL, getCurrentWebView().getUrl());

		mActivity.startActivity(i);
	}

	@Override
	public void shareCurrentPage() {
		WebView webView = getCurrentWebView();

		if (webView != null) {
			ApplicationUtils.sharePage(mActivity, webView.getTitle(), webView.getUrl());
		}
	}

	@Override
	public void startSearch() {
		WebView webView = getCurrentWebView();

		if (webView != null) {
			// webView.showFindDialog(null, true);
		}
	}

	@Override
	public void clearFormData() {
		WebViewDatabase.getInstance(mActivity).clearFormData();
		getCurrentWebView().clearFormData();
	}

	@Override
	public void clearCache() {
		getCurrentWebView().clearCache(true);
	}

	@Override
	public void setHttpAuthUsernamePassword(String host, String realm, String username, String password) {
		getCurrentWebView().setHttpAuthUsernamePassword(host, realm, username, password);
	}

	@Override
	public CustomWebView getWebViewByTabId(UUID tabId) {
		BaseWebViewFragment fragment = getWebViewFragmentByUUID(tabId);
		if (fragment != null) {
			return fragment.getWebView();
		} else {
			return null;
		}
	}

	@Override
	public void setUploadMessage(ValueCallback<Uri> uploadMsg) {
		mUploadMessage = uploadMsg;
	}

	@Override
	public ValueCallback<Uri> getUploadMessage() {
		return mUploadMessage;
	}

	@Override
	public void onNewIntent(Intent intent) {
		if (intent != null) {
			if (Intent.ACTION_VIEW.equals(intent.getAction()) || Intent.ACTION_MAIN.equals(intent.getAction())) {
				// ACTION_VIEW and ACTION_MAIN can specify an url to load.
				String url = intent.getDataString();

				if (!TextUtils.isEmpty(url)) {
					if (!isCurrentTabReusable()) {
						addTab(url,
								false,
								PreferenceManager.getDefaultSharedPreferences(this.getMainActivity()).getBoolean(Constants.PREFERENCE_INCOGNITO_BY_DEFAULT,
										false), false);
					} else {
						loadUrl(url);
					}
				} else {
					// We do not have an url. Open a new tab if there is no tab currently opened,
					// else do nothing.
					if (getTabCount() <= 0) {
						addTab(true,
								PreferenceManager.getDefaultSharedPreferences(this.getMainActivity()).getBoolean(Constants.PREFERENCE_INCOGNITO_BY_DEFAULT,
										false), false);
					}
				}
			} else if (Constants.ACTION_BROWSER_CONTEXT_MENU.equals(intent.getAction())) {
				if (intent.hasExtra(Constants.EXTRA_ACTION_ID)) {
					int actionId = intent.getIntExtra(Constants.EXTRA_ACTION_ID, -1);

					switch (actionId) {
					case SealBrowserActivity.CONTEXT_MENU_OPEN:
						if (HitTestResult.SRC_IMAGE_ANCHOR_TYPE == intent.getIntExtra(Constants.EXTRA_HIT_TEST_RESULT, -1)) {
							requestHrefNode(SealBrowserActivity.CONTEXT_MENU_OPEN);
						} else {
							loadUrl(intent.getStringExtra(Constants.EXTRA_URL));
						}
						break;

					case SealBrowserActivity.CONTEXT_MENU_OPEN_IN_NEW_TAB:

						if (HitTestResult.SRC_IMAGE_ANCHOR_TYPE == intent.getIntExtra(Constants.EXTRA_HIT_TEST_RESULT, -1)) {
							requestHrefNode(SealBrowserActivity.CONTEXT_MENU_OPEN_IN_NEW_TAB, intent.getBooleanExtra(Constants.EXTRA_INCOGNITO, false));
						} else {
							addTab(intent.getStringExtra(Constants.EXTRA_URL), false, intent.getBooleanExtra(Constants.EXTRA_INCOGNITO, false), false);
						}
						break;

					case SealBrowserActivity.CONTEXT_MENU_OPEN_IN_BACKGROUND:

						if (HitTestResult.SRC_IMAGE_ANCHOR_TYPE == intent.getIntExtra(Constants.EXTRA_HIT_TEST_RESULT, -1)) {
							requestHrefNode(SealBrowserActivity.CONTEXT_MENU_OPEN_IN_BACKGROUND, intent.getBooleanExtra(Constants.EXTRA_INCOGNITO, false));
						} else {
							addTab(intent.getStringExtra(Constants.EXTRA_URL), true, intent.getBooleanExtra(Constants.EXTRA_INCOGNITO, false), false);
						}
						break;

					case SealBrowserActivity.CONTEXT_MENU_COPY:
						if (HitTestResult.SRC_IMAGE_ANCHOR_TYPE == intent.getIntExtra(Constants.EXTRA_HIT_TEST_RESULT, -1)) {
							requestHrefNode(SealBrowserActivity.CONTEXT_MENU_COPY);
						} else {
							ApplicationUtils.copyTextToClipboard(mActivity, intent.getStringExtra(Constants.EXTRA_URL),
									mActivity.getResources().getString(R.string.UrlCopyToastMessage));
						}
						break;

					case SealBrowserActivity.CONTEXT_MENU_DOWNLOAD:
						if (HitTestResult.SRC_IMAGE_ANCHOR_TYPE == intent.getIntExtra(Constants.EXTRA_HIT_TEST_RESULT, -1)) {
							requestHrefNode(SealBrowserActivity.CONTEXT_MENU_DOWNLOAD);
						} else {
							try {
								DownloadItem item = new DownloadItem(intent.getStringExtra(Constants.EXTRA_URL));

								long id = ((DownloadManager) mActivity.getSystemService(Context.DOWNLOAD_SERVICE)).enqueue(item);
								item.setId(id);

								Controller.getInstance().getDownloadsList().add(item);

								Toast.makeText(mActivity, String.format(mActivity.getString(R.string.DownloadStart), item.getFileName()), Toast.LENGTH_SHORT)
										.show();
							} catch (IllegalArgumentException e) {
								Toast.makeText(mActivity, mActivity.getString(R.string.DownloadErrorNotSupportedScheme), Toast.LENGTH_SHORT).show();
							} catch (NullPointerException e) {
								Toast.makeText(mActivity, mActivity.getString(R.string.DownloadErrorInvalidUrl), Toast.LENGTH_SHORT).show();
							}
						}
						break;

					case SealBrowserActivity.CONTEXT_MENU_SHARE:
						if (HitTestResult.SRC_IMAGE_ANCHOR_TYPE == intent.getIntExtra(Constants.EXTRA_HIT_TEST_RESULT, -1)) {
							requestHrefNode(SealBrowserActivity.CONTEXT_MENU_SHARE);
						} else {
							ApplicationUtils.sharePage(mActivity, null, intent.getStringExtra(Constants.EXTRA_URL));
						}
						break;

					default:
						if (HitTestResult.SRC_IMAGE_ANCHOR_TYPE == intent.getIntExtra(Constants.EXTRA_HIT_TEST_RESULT, -1)) {
							requestHrefNode(actionId);
						} else {
							Controller
									.getInstance()
									.getAddonManager()
									.onContributedContextLinkMenuItemSelected(mActivity, actionId, intent.getIntExtra(Constants.EXTRA_HIT_TEST_RESULT, -1),
											intent.getStringExtra(Constants.EXTRA_URL), getCurrentWebView());
						}
						break;
					}
				}
			}
		} else {
			addTab(true, false, false);
		}
	}

	@Override
	public boolean onKeyBack() {
		if (mCustomView != null) {
			onHideCustomView();
			return true;
		}

		return false;
	}

	@Override
	public void onPageFinished(final WebView view, final String url) {

		if (mHomePageLoading) {
			mHomePageLoading = false;

			if (PreferenceManager.getDefaultSharedPreferences(mActivity).getBoolean(Constants.TECHNICAL_PREFERENCE_HOMEPAGE_URL_UPDATE_NEEDED, false)) {
				Editor editor = PreferenceManager.getDefaultSharedPreferences(mActivity).edit();
				editor.putBoolean(Constants.TECHNICAL_PREFERENCE_HOMEPAGE_URL_UPDATE_NEEDED, false);
				editor.putString(Constants.PREFERENCE_HOME_PAGE, url);
				editor.commit();
			}
		}

		view.postDelayed(new Runnable() {

			@Override
			public void run() {
				if (BookmarksWrapper.urlHasBookmark(mActivity.getContentResolver(), url, view.getOriginalUrl())) {
					Picture p = view.capturePicture();

					new ThumbnailSaver(mActivity.getContentResolver(), url, view.getOriginalUrl(), p, ApplicationUtils
							.getBookmarksThumbnailsDimensions(mActivity)).execute();
				}
			}
		}, 2000);
	}

	@Override
	public void onClientPageFinished(CustomWebView view, String url) {
	}

	@Override
	public void onReceivedIcon(WebView view, Bitmap icon) {
		if (view == getCurrentWebView()) {
			setApplicationButtonImage(icon);
		}
	}

	@Override
	public void onMainActivityPause() {
		CustomWebView webView = getCurrentWebView();
		if (webView != null) {
			webView.pauseTimers();
		}
	}

	@Override
	public void onMainActivityResume() {
		CustomWebView webView = getCurrentWebView();
		if (webView != null) {
			webView.resumeTimers();
		}
	}

	@Override
	public void onShowCustomView(View view, int requestedOrientation, CustomViewCallback callback) {
		if (mCustomView != null) {
			callback.onCustomViewHidden();
			return;
		}

		if (requestedOrientation == -1) {
			requestedOrientation = mActivity.getRequestedOrientation();
		}

		mOriginalOrientation = mActivity.getRequestedOrientation();
		FrameLayout decor = (FrameLayout) mActivity.getWindow().getDecorView();
		mFullscreenContainer = new FullscreenHolder(mActivity);
		mFullscreenContainer.addView(view, COVER_SCREEN_PARAMS);
		decor.addView(mFullscreenContainer, COVER_SCREEN_PARAMS);
		mCustomView = view;

		mCustomViewCallback = callback;
		mActivity.setRequestedOrientation(requestedOrientation);
	}

	@Override
	public void onHideCustomView() {
		if (mCustomView == null) return;

		FrameLayout decor = (FrameLayout) mActivity.getWindow().getDecorView();
		decor.removeView(mFullscreenContainer);
		mFullscreenContainer = null;
		mCustomView = null;
		mCustomViewCallback.onCustomViewHidden();
		// Show the content view.
		mActivity.setRequestedOrientation(mOriginalOrientation);
	}

	@Override
	public void onGeolocationPermissionsShowPrompt(String origin, Callback callback) {
		if (mGeolocationPermissionsDialog == null) {
			mGeolocationPermissionsDialog = new GeolocationPermissionsDialog(mActivity);
		}

		mGeolocationPermissionsDialog.initialize(origin, callback);
		mGeolocationPermissionsDialog.show();

	}

	@Override
	public void onGeolocationPermissionsHidePrompt() {
		if (mGeolocationPermissionsDialog != null) {
			mGeolocationPermissionsDialog.hide();
		}
	}

	@Override
	public void loadUrl(BaseWebViewFragment webViewFragment, String url) {
		CustomWebView webView = webViewFragment.getWebView();

		if (Constants.URL_ABOUT_START.equals(url)) {
			showStartPage(webViewFragment);

			// Check if there is no pb with this.
			// This recreate a new WebView, because i cannot found a way
			// to reset completely (history and display) a WebView.
			webViewFragment.resetWebView();
		} else {
			hideStartPage(webViewFragment);
			webView.loadUrl(url);
		}

		webView.requestFocus();
	}

	@Override
	public boolean isFullScreen() {
		return PreferenceManager.getDefaultSharedPreferences(mActivity).getBoolean(Constants.PREFERENCE_FULL_SCREEN, false);
	}

	@Override
	public void toggleFullScreen() {
		boolean newValue = !isFullScreen();
		Editor editor = PreferenceManager.getDefaultSharedPreferences(mActivity).edit();
		editor.putBoolean(Constants.PREFERENCE_FULL_SCREEN, newValue);
		editor.commit();

		setFullScreenFromPreferences();
	}

	@Override
	public void saveTabs() {
		String userStartPage = PreferenceManager.getDefaultSharedPreferences(mActivity).getString(Constants.PREFERENCE_HOME_PAGE, Constants.URL_ABOUT_START);

		Set<String> tabs = new HashSet<String>();

		for (BaseWebViewFragment f : getTabsFragments()) {
			if (!f.isStartPageShown() && !f.isWebViewOnUrl(userStartPage) && !f.isPrivateBrowsingEnabled()) {
				tabs.add(f.getWebView().getUrl());
			}
		}

		Editor editor = PreferenceManager.getDefaultSharedPreferences(mActivity).edit();
		editor.putStringSet(Constants.TECHNICAL_PREFERENCE_SAVED_TABS, tabs);
		editor.commit();
	}

	protected abstract Collection<BaseWebViewFragment> getTabsFragments();

	protected abstract void setFullScreenFromPreferences();

	protected boolean isStartPageShownOnCurrentTab() {
		BaseWebViewFragment currentWebViewFragment = getCurrentWebViewFragment();
		return currentWebViewFragment != null && currentWebViewFragment.isStartPageShown();
	}

	protected boolean isHomePageStartPage() {
		return Constants.URL_ABOUT_START.equals(PreferenceManager.getDefaultSharedPreferences(mActivity).getString(Constants.PREFERENCE_HOME_PAGE,
				Constants.URL_ABOUT_START));
	}

	private void startHandler() {
		mHandler = new Handler() {

			@Override
			public void handleMessage(Message msg) {
				switch (msg.what) {
				case FOCUS_NODE_HREF:
					String url = (String) msg.getData().get("url");
					String src = (String) msg.getData().get("src");

					if (url == "") {
						url = src;
					}

					if (TextUtils.isEmpty(url)) {
						break;
					}

					switch (msg.arg1) {
					case SealBrowserActivity.CONTEXT_MENU_OPEN:
						loadUrl(url);
						break;

					case SealBrowserActivity.CONTEXT_MENU_OPEN_IN_NEW_TAB:
						addTab(url, false, msg.arg2 > 0 ? true : false, false);
						break;

					case SealBrowserActivity.CONTEXT_MENU_OPEN_IN_BACKGROUND:
						addTab(url, true, msg.arg2 > 0 ? true : false, false);
						break;

					case SealBrowserActivity.CONTEXT_MENU_COPY:
						ApplicationUtils.copyTextToClipboard(mActivity, url, mActivity.getResources().getString(R.string.UrlCopyToastMessage));
						break;

					case SealBrowserActivity.CONTEXT_MENU_DOWNLOAD:
						/*DownloadItem item = new DownloadItem(url);

						long id = ((DownloadManager) mActivity.getSystemService(Context.DOWNLOAD_SERVICE)).enqueue(item);
						item.setId(id);

						Controller.getInstance().getDownloadsList().add(item);
						Toast.makeText(mActivity, String.format(mActivity.getString(R.string.DownloadStart), item.getFileName()), Toast.LENGTH_SHORT).show();
						*/
						break;

					case SealBrowserActivity.CONTEXT_MENU_SHARE:
						ApplicationUtils.sharePage(mActivity, null, url);
						break;

					default:
						Controller.getInstance().getAddonManager()
								.onContributedContextLinkMenuItemSelected(mActivity, msg.arg1, HitTestResult.SRC_IMAGE_ANCHOR_TYPE, url, getCurrentWebView());
						break;
					}
					break;
				default:
					super.handleMessage(msg);
				}
			}

		};
	}

	private void requestHrefNode(int action) {
		requestHrefNode(action, false);
	}

	private void requestHrefNode(int action, boolean incognito) {
		WebView webView = getCurrentWebView();

		if (webView != null) {
			final HashMap<String, WebView> hrefMap = new HashMap<String, WebView>();
			hrefMap.put("webview", webView);

			final Message msg = mHandler.obtainMessage(FOCUS_NODE_HREF, action, incognito ? 1 : 0, hrefMap);

			webView.requestFocusNodeHref(msg);
		}
	}

	/**
	 * Check if the current tab can be reused to display an intent request. A tab is reusable if it is on the user-defined start page.
	 * 
	 * @return True if the current tab can be reused.
	 */
	private boolean isCurrentTabReusable() {
		String homePageUrl = PreferenceManager.getDefaultSharedPreferences(mActivity).getString(Constants.PREFERENCE_HOME_PAGE, Constants.URL_ABOUT_START);
		BaseWebViewFragment currentWebViewFragment = getCurrentWebViewFragment();
		CustomWebView currentWebView = getCurrentWebView();

		return (currentWebViewFragment != null && currentWebViewFragment.isStartPageShown())
				|| (currentWebView != null && homePageUrl != null && homePageUrl.equals(currentWebView.getUrl()));
	}

	static class FullscreenHolder extends FrameLayout {

		public FullscreenHolder(Context ctx) {
			super(ctx);
			setBackgroundColor(ctx.getResources().getColor(android.R.color.black));
		}

		@Override
		public boolean onTouchEvent(MotionEvent evt) {
			return true;
		}

	}

}
